package com.douziit.meilianoa.base.exception;


import com.douziit.meilianoa.base.enums.ResponseCode;
import com.douziit.meilianoa.base.response.ResponseUtil;
import com.mysql.jdbc.MysqlDataTruncation;
import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartException;

import java.io.IOException;
import java.util.List;

/**
 * 自定义Controller增强型异常处理
 * 可以根据不同的异常类型返回详细的异常结果
 *
 * @author MrShun
 * @version 1.0
 * @Date 2017/11/10
 */
@ControllerAdvice
public class ExceptionDefine {

    private final Logger logger = LoggerFactory.getLogger(ExceptionDefine.class);

    /**
     * 运行时业务提示异常
     */
    @ExceptionHandler(TipException.class)
    @ResponseBody
    public Object tipException(TipException ex) {
        String msg = ex.getMessage();
        logger.error(msg);
        ResponseCode resp = ex.getResponseCode();
        if (resp == null) {
            return ResponseUtil.jsonFail(ex.getCode(), msg);
        }
        return ResponseUtil.jsonFail(resp);
    }

    /**
     * Redis链接失败
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(RedisConnectionFailureException.class)
    @ResponseBody
    public Object redisConnectionFailureException(RedisConnectionFailureException ex) {
        logger.error("",ex);
        return ResponseUtil.jsonFail(ResponseCode.SYSTEM_ERROR, "Redis服务连接出现异常!");
    }


    /**
     * 操作数据库出现异常, 某个字段内容超出异常
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(MysqlDataTruncation.class)
    @ResponseBody
    public Object mysqlDataTruncation(MysqlDataTruncation ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonFail(ResponseCode.CONTENT_OVER_ERROR, ex.getMessage());
    }

    /**
     * 操作数据库出现异常, 缺少数据库字段值
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(DataIntegrityViolationException.class)
    @ResponseBody
    public Object mysqlDataTruncation(DataIntegrityViolationException ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonFail(ResponseCode.MISSING_FIELD_ERROR, ex.getMessage());
    }

    /**
     * 操作数据库出现异常, 约束异常
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(MySQLIntegrityConstraintViolationException.class)
    @ResponseBody
    public Object mySQLIntegrityConstraintViolationException(MySQLIntegrityConstraintViolationException ex) {
        logger.error("",ex);
        return ResponseUtil.jsonException(ResponseCode.RELATION_ERROR, ex.getMessage());
    }

    /**
     * 空指针异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public Object nullPointerExceptionHandler(NullPointerException ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonException(ResponseCode.NULL_ERROR, ex.getMessage());
    }

    /**
     * Request 请求类型异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    public Object httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonFail(ResponseCode.REQUEST_TYPE_ERROR, ex.getMessage());
    }


    /**
     * 传递的参数异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    public Object illegalArgumentException(IllegalArgumentException ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonException(ResponseCode.PARAMS_ERROR, ex.getMessage());
    }

    /**
     * 类型转换异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ClassCastException.class)
    @ResponseBody
    public Object classCastExceptionHandler(ClassCastException ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonException(ResponseCode.CONVERT_ERROR, ex.getMessage());
    }

    /**
     * IO异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(IOException.class)
    @ResponseBody
    public Object iOExceptionHandler(IOException ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonException(ResponseCode.IO_ERROR, ex.getMessage());
    }

    /**
     * 未知方法异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(NoSuchMethodException.class)
    @ResponseBody
    public Object noSuchMethodExceptionHandler(NoSuchMethodException ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonException(ResponseCode.METHOD_ERROR, ex.getMessage());
    }

    /**
     * 数组越界异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(IndexOutOfBoundsException.class)
    @ResponseBody
    public Object indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonException(ResponseCode.ARRAY_ERROR, ex.getMessage());
    }

    /**
     * 上传文件出现异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MultipartException.class)
    @ResponseBody
    public Object multipartException(MultipartException ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonException(ResponseCode.FILE_UPLOAD_ERROR, ex.getMessage());
    }

    /**
     * 400错误
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({HttpMessageNotReadableException.class})
    @ResponseBody
    public Object requestNotReadable(HttpMessageNotReadableException ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonException(ResponseCode.PARSE_ERROR, ex.getMessage());
    }

    /**
     * 400错误
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({TypeMismatchException.class})
    @ResponseBody
    public Object requestTypeMismatch(TypeMismatchException ex) {
        logger.error("",ex);
        ex.printStackTrace();
        return ResponseUtil.jsonException(ResponseCode.MATH_ERROR, ex.getMessage());
    }

    /**
     * 400错误
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({MissingServletRequestParameterException.class})
    @ResponseBody
    public Object requestMissingServletRequest(MissingServletRequestParameterException ex) {
        logger.error("",ex);
        return ResponseUtil.jsonException(ResponseCode.BIND_ERROR, ex.getMessage());
    }

    /**
     * 415 - Unsupported Media Type
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler({HttpMediaTypeNotAcceptableException.class})
    @ResponseBody
    public Object request406() {
        return ResponseUtil.jsonException(ResponseCode.MIDEA_TYPE_ERROR);
    }

    /**
     * 服务异常
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler({ConversionNotSupportedException.class, HttpMessageNotWritableException.class})
    @ResponseBody
    public Object server500(RuntimeException ex) {
        logger.error(ex.getMessage());
        ex.printStackTrace();
        return ResponseUtil.jsonException(ResponseCode.SYSTEM_ERROR, ex.getMessage());
    }

    /**
     * hibernate validate 校验器异常
     *
     * @param ex
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public Object methodArgumentNotValidException(MethodArgumentNotValidException ex) {
        List<FieldError> errors = ex.getBindingResult().getFieldErrors();
        FieldError error = errors.get(0);
        String message = error.getField() + " " + error.getDefaultMessage();
        return ResponseUtil.jsonFail(message);
    }


    /**
     * hibernate validate 校验器异常
     *
     * @param ex
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public Object bindException(BindException ex) {
        List<FieldError> errors = ex.getBindingResult().getFieldErrors();
        FieldError error = errors.get(0);
        String message = error.getField() + " " + error.getDefaultMessage();
        return ResponseUtil.jsonFail(message);
    }


    /***
     * 提交方式异常
     * @param ex
     * @return
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseBody
    public Object httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException ex) {
        logger.error("",ex);
        return ResponseUtil.jsonFail(ResponseCode.FORM_TYPE_ERROR);
    }



    /****
     * 代码或其他异常
     * @param ex
     * @return
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Object exception(Exception ex) {
        logger.error("",ex);
        return ResponseUtil.jsonFail(ResponseCode.SYSTEM_ERROR);
    }



}
